#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
import json
import time
import requests
import shutil
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QLabel, QProgressBar, QTextEdit, QLineEdit,
    QFileDialog, QMessageBox, QGroupBox, QFormLayout, QSpinBox, QScrollBar, QCheckBox, QComboBox, QStackedWidget, QTabWidget
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QMutex
from PyQt5.QtGui import QTextCursor
import configparser
from duplicate_cleaner import DuplicateCleanerDialog
# 导入视频搬运工具
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from video_mover import VideoMoverTool

# 确保在Windows上也能正确处理编码
if os.name == 'nt':
    # Windows系统
    import locale
    if locale.getpreferredencoding() != 'utf-8':
        # 设置环境变量以支持UTF-8
        os.environ['PYTHONIOENCODING'] = 'utf-8'


class VideoProcessor(QThread):
    # 定义信号
    progress_updated = pyqtSignal(int, int, str)  # 当前进度, 总数, 当前文件名
    log_message = pyqtSignal(str)  # 日志消息
    processing_finished = pyqtSignal()  # 处理完成信号
    error_occurred = pyqtSignal(str)  # 错误信号

    def __init__(self, config):
        super().__init__()
        self.config = config
        self.is_running = False
        self.log_mutex = QMutex()  # 用于线程安全的日志记录
        
        # 统计信息
        self.processed_count = 0
        self.success_count = 0
        self.api_fail_count = 0
        self.category_uncertain_count = 0
        self.other_fail_count = 0

    def stop(self):
        self.is_running = False

    def run(self):
        try:
            self.process_videos()
        except Exception as e:
            self.error_occurred.emit(str(e))
        finally:
            self.processing_finished.emit()

    def process_videos(self):
        # 获取配置
        source_folder = self.config.get('paths', 'source_folder')
        processed_folder = self.config.get('paths', 'processed_folder')
        request_interval = self.config.getint('api', 'request_interval')
        max_workers = self.config.getint('threading', 'max_workers', fallback=1)
        enable_smart_category = self.config.getboolean('processing', 'enable_smart_category', fallback=True)
        
        # 初始化统计信息
        self.processed_count = 0
        self.success_count = 0
        self.api_fail_count = 0
        self.category_uncertain_count = 0
        self.other_fail_count = 0

        # 创建已处理文件夹
        os.makedirs(processed_folder, exist_ok=True)

        # 根据是否启用智能类目决定文件夹结构
        if enable_smart_category:
            # 智能类目模式：创建12个类目文件夹及佣金率区间子文件夹
            categories = [
                "食品饮料", "家居百货", "女装女鞋", "美妆护肤",
                "个护清洁", "医疗保健", "母婴玩具", "茶酒生鲜",
                "男装男鞋", "运动户外", "数码家电", "珠宝配饰"
            ]
            
            # 佣金率区间定义
            commission_ranges = [
                "15-19", "20-24", "25-29", "30-34", "35-39", 
                "40-44", "45-49", "50-54", "55-59", "其他"
            ]
            
            # 创建类目文件夹和佣金率区间子文件夹
            for category in categories:
                category_path = os.path.join(processed_folder, category)
                os.makedirs(category_path, exist_ok=True)
                
                # 在每个类目下创建佣金率区间文件夹
                for commission_range in commission_ranges:
                    range_path = os.path.join(category_path, commission_range)
                    os.makedirs(range_path, exist_ok=True)
        else:
            # 简单模式：根据源文件夹名称创建目标文件夹和佣金率区间子文件夹
            source_folder_name = os.path.basename(source_folder.rstrip('/\\'))
            target_folder = os.path.join(processed_folder, source_folder_name)
            os.makedirs(target_folder, exist_ok=True)
            
            # 佣金率区间定义
            commission_ranges = [
                "15-19", "20-24", "25-29", "30-34", "35-39", 
                "40-44", "45-49", "50-54", "55-59", "其他"
            ]
            
            # 在目标文件夹下创建佣金率区间文件夹
            for commission_range in commission_ranges:
                range_path = os.path.join(target_folder, commission_range)
                os.makedirs(range_path, exist_ok=True)
        
        # 创建失败处理文件夹
        self.create_failure_folders(processed_folder)

        # 递归获取所有视频文件
        video_files_with_paths = self._get_all_video_files(source_folder)
        total_files = len(video_files_with_paths)
        
        # 检查是否找到视频文件
        if total_files == 0:
            self.log_message.emit("⚠️  未找到任何视频文件！")
            self.log_message.emit(f"请检查源文件夹是否包含 .mp4 格式的视频文件: {source_folder}")
            self.log_message.emit("提示：程序会递归搜索所有子文件夹中的视频文件")
            self.log_message.emit("💡 建议：请添加视频文件后重新开始处理")
            # 发送完成信号，停止任务
            self.progress_updated.emit(0, 0, "未找到视频文件 - 任务已停止")
            self.processing_finished.emit()  # 发送处理完成信号
            return
        
        self.log_message.emit(f"✅ 找到 {total_files} 个视频文件（包括子文件夹）")
        
        self.is_running = True
        
        if max_workers == 1:
            # 单线程处理
            self._process_videos_single_thread(video_files_with_paths, total_files, source_folder, processed_folder, request_interval, enable_smart_category)
        else:
            # 多线程处理
            self._process_videos_multi_thread(video_files_with_paths, total_files, source_folder, processed_folder, request_interval, max_workers, enable_smart_category)
        
        # 输出统计信息
        self.log_message.emit(f"\n\n=== 处理统计 ===")
        self.log_message.emit(f"总处理文件: {self.processed_count}")
        self.log_message.emit(f"✅ 成功处理: {self.success_count}")
        self.log_message.emit(f"⚠️ 快手API失败: {self.api_fail_count}")
        self.log_message.emit(f"⚠️ 不确定类目: {self.category_uncertain_count}")
        self.log_message.emit(f"⚠️ 其他失败: {self.other_fail_count}")
        
        self.progress_updated.emit(total_files, total_files, "完成")
    
    def _get_all_video_files(self, source_folder):
        """递归获取所有视频文件（包括子文件夹）"""
        video_files_with_paths = []
        
        for root, dirs, files in os.walk(source_folder):
            for file in files:
                if file.endswith('.mp4'):
                    # 返回完整路径和相对路径的元组
                    full_path = os.path.join(root, file)
                    relative_path = os.path.relpath(full_path, source_folder)
                    video_files_with_paths.append((full_path, file, relative_path))
        
        return video_files_with_paths
    
    def create_failure_folders(self, processed_folder):
        """创建失败处理文件夹结构"""
        failure_folder = os.path.join(processed_folder, "处理失败")
        os.makedirs(failure_folder, exist_ok=True)
        
        # 创建三个子文件夹
        failure_subfolders = [
            "快手api返回数据失败的视频",
            "不确定类目的视频", 
            "其他失败"
        ]
        
        for subfolder in failure_subfolders:
            os.makedirs(os.path.join(failure_folder, subfolder), exist_ok=True)
    
    def _process_videos_single_thread(self, video_files_with_paths, total_files, source_folder, processed_folder, request_interval, enable_smart_category):
        """单线程处理视频文件"""
        for i, (full_path, filename, relative_path) in enumerate(video_files_with_paths):
            if not self.is_running:
                break
            
            self.processed_count += 1
            # 更新进度（显示相对路径）
            self.progress_updated.emit(i + 1, total_files, relative_path)
            
            # 处理单个文件
            success = self._process_single_video(full_path, filename, relative_path, processed_folder, enable_smart_category, source_folder)
            
            if success:
                self.success_count += 1
            
            # 等待指定时间间隔
            time.sleep(request_interval)
    
    def _process_videos_multi_thread(self, video_files_with_paths, total_files, source_folder, processed_folder, request_interval, max_workers, enable_smart_category):
        """多线程处理视频文件"""
        self.log_message.emit(f"启用多线程模式，线程数: {max_workers}")
        
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            # 提交任务
            future_to_data = {
                executor.submit(self._process_single_video_with_delay, full_path, filename, relative_path, processed_folder, request_interval, i, enable_smart_category, source_folder): (full_path, filename, relative_path)
                for i, (full_path, filename, relative_path) in enumerate(video_files_with_paths)
            }
            
            # 处理结果
            for future in as_completed(future_to_data):
                if not self.is_running:
                    # 取消所有未完成的任务
                    for f in future_to_data:
                        f.cancel()
                    break
                
                full_path, filename, relative_path = future_to_data[future]
                self.processed_count += 1
                
                try:
                    success = future.result()
                    if success:
                        self.success_count += 1
                    
                    # 更新进度
                    self.progress_updated.emit(self.processed_count, total_files, relative_path)
                    
                except Exception as e:
                    self.log_message.emit(f"线程处理文件出错 {relative_path}: {str(e)}")
                    self.other_fail_count += 1
    
    def _process_single_video_with_delay(self, full_path, filename, relative_path, processed_folder, request_interval, index, enable_smart_category, source_folder):
        """带延迟的单文件处理（用于多线程）"""
        # 为了避免同时发起太多请求，根据线程索引添加随机延迟
        import random
        delay = (index % 5) * request_interval + random.uniform(0, 2)  # 添加一些随机延迟
        time.sleep(delay)
        
        return self._process_single_video(full_path, filename, relative_path, processed_folder, enable_smart_category, source_folder)
    
    def _process_single_video(self, full_path, filename, relative_path, processed_folder, enable_smart_category, source_folder):
        """处理单个视频文件"""
        try:
            # 解析文件名获取商品ID和标题
            result = self.parse_filename(filename)
            if result is None or len(result) != 2:
                self._safe_log(f"无法解析文件名: {relative_path}")
                self._move_to_failure_folder(full_path, filename, processed_folder, "其他失败")
                self.other_fail_count += 1
                return False
                
            product_id, title = result
            if not product_id or not title:
                self._safe_log(f"无法解析文件名: {relative_path}")
                self._move_to_failure_folder(full_path, filename, processed_folder, "其他失败")
                self.other_fail_count += 1
                return False
            
            # 获取佣金率
            self._safe_log(f"开始获取佣金率: 商品ID={product_id}, 文件: {relative_path}")
            commission_rate = self.get_commission_rate(product_id)
            if not commission_rate:
                self._safe_log(f"无法获取佣金率: {relative_path}")
                self._move_to_failure_folder(full_path, filename, processed_folder, "快手api返回数据失败的视频")
                self.api_fail_count += 1
                return False
            self._safe_log(f"成功获取佣金率: {commission_rate}%, 商品ID={product_id}, 文件: {relative_path}")
            
            # 根据是否启用智能类目决定处理方式
            if enable_smart_category:
                # 智能类目模式：获取商品类目
                category = self.get_product_category(title)
                if not category or category == "不确定":
                    self._safe_log(f"无法确定类目: {relative_path}")
                    self._move_to_failure_folder(full_path, filename, processed_folder, "不确定类目的视频")
                    self.category_uncertain_count += 1
                    return False
                
                # 确定佣金率区间
                commission_range = self._get_commission_range(commission_rate)
                
                # 确定目标文件夹
                target_folder = os.path.join(processed_folder, category, commission_range)
                
                self._safe_log(f"成功处理: {relative_path} -> {category}/{commission_range} (佣金率: {commission_rate}%)")
            else:
                # 简单模式：只按佣金率分类
                # 使用文件的子文件夹路径作为分类依据，但要确保不移动到根目录
                source_folder_name = os.path.basename(source_folder.rstrip('/\\'))
                subfolder_path = os.path.dirname(relative_path)
                # 如果relative_path没有目录部分，则使用源文件夹名称
                if not subfolder_path or subfolder_path == ".":
                    subfolder_path = source_folder_name
                
                commission_range = self._get_commission_range(commission_rate)
                
                # 确定目标文件夹
                target_folder = os.path.join(processed_folder, subfolder_path, commission_range)
                
                self._safe_log(f"成功处理: {relative_path} -> {subfolder_path}/{commission_range} (佣金率: {commission_rate}%)")
            
            # 确保目标文件夹存在
            os.makedirs(target_folder, exist_ok=True)
            
            # 移动文件
            target_path = os.path.join(target_folder, filename)
            
            # 处理重名文件
            if os.path.exists(target_path):
                base_name, ext = os.path.splitext(filename)
                counter = 1
                new_filename = f"{base_name}_{counter}{ext}"
                target_path = os.path.join(target_folder, new_filename)
                while os.path.exists(target_path):
                    counter += 1
                    new_filename = f"{base_name}_{counter}{ext}"
                    target_path = os.path.join(target_folder, new_filename)
            
            # 移动文件
            shutil.move(full_path, target_path)
            return True
            
        except Exception as e:
            self._safe_log(f"处理文件出错 {relative_path}: {str(e)}")
            self._move_to_failure_folder(full_path, filename, processed_folder, "其他失败")
            self.other_fail_count += 1
            return False
    
    def _get_commission_range(self, commission_rate):
        """根据佣金率确定区间"""
        try:
            rate = float(commission_rate)
            if 15 <= rate <= 19:
                return "15-19"
            elif 20 <= rate <= 24:
                return "20-24"
            elif 25 <= rate <= 29:
                return "25-29"
            elif 30 <= rate <= 34:
                return "30-34"
            elif 35 <= rate <= 39:
                return "35-39"
            elif 40 <= rate <= 44:
                return "40-44"
            elif 45 <= rate <= 49:
                return "45-49"
            elif 50 <= rate <= 54:
                return "50-54"
            elif 55 <= rate <= 59:
                return "55-59"
            else:
                # 生成日志提醒：当佣金率移动至"其他"文件夹时
                self._safe_log(f"⚠️  佣金率 {commission_rate}% 不在预定义范围内，已移动至'其他'文件夹")
                return "其他"
        except (ValueError, TypeError):
            # 生成日志提醒：当佣金率移动至"其他"文件夹时
            self._safe_log(f"⚠️  佣金率 {commission_rate} 无法解析为数字，已移动至'其他'文件夹")
            return "其他"
    
    def _move_to_failure_folder(self, full_path, filename, processed_folder, failure_type):
        """移动文件到失败处理文件夹"""
        try:
            if not os.path.exists(full_path):
                return  # 文件不存在，可能已经被其他线程处理
            
            failure_folder = os.path.join(processed_folder, "处理失败", failure_type)
            os.makedirs(failure_folder, exist_ok=True)  # 确保文件夹存在
            target_path = os.path.join(failure_folder, filename)
            
            # 处理重名文件
            if os.path.exists(target_path):
                base_name, ext = os.path.splitext(filename)
                counter = 1
                new_filename = f"{base_name}_{counter}{ext}"
                target_path = os.path.join(failure_folder, new_filename)
                while os.path.exists(target_path):
                    counter += 1
                    new_filename = f"{base_name}_{counter}{ext}"
                    target_path = os.path.join(failure_folder, new_filename)
            
            shutil.move(full_path, target_path)
        except Exception as e:
            self._safe_log(f"移动失败文件出错 {filename}: {str(e)}")
    
    def _safe_log(self, message):
        """线程安全的日志记录"""
        self.log_mutex.lock()
        try:
            self.log_message.emit(message)
        finally:
            self.log_mutex.unlock()

    def parse_filename(self, filename):
        """解析文件名获取商品ID和标题"""
        # 文件名格式: 商品id100001 视频标题xxxx.mp4
        # 处理不同的文件名格式变体
        
        # 情况1: 标准格式 "商品id{ID} 视频标题{标题}.mp4"
        if filename.startswith("商品id") and " 视频标题" in filename:
            try:
                # 提取商品ID
                id_part = filename.split(" 视频标题")[0]
                product_id = id_part.replace("商品id", "")
                
                # 提取标题（去掉.mp4扩展名）
                title_part = filename.split(" 视频标题")[1]
                title = title_part.replace(".mp4", "")
                
                return product_id, title
            except Exception as e:
                self.log_message.emit(f"解析标准格式文件名出错: {filename}, 错误: {str(e)}")
        
        # 情况2: 紧凑格式 "商品id{ID}视频标题{标题}.mp4" (没有空格)
        elif filename.startswith("商品id") and "视频标题" in filename:
            try:
                # 提取商品ID
                id_part = filename.split("视频标题")[0]
                product_id = id_part.replace("商品id", "")
                
                # 提取标题（去掉.mp4扩展名）
                title_part = filename.split("视频标题")[1]
                title = title_part.replace(".mp4", "")
                
                return product_id, title
            except Exception as e:
                self.log_message.emit(f"解析紧凑格式文件名出错: {filename}, 错误: {str(e)}")
        
        # 情况3: 商品ID后有空格的情况 "商品 id{ID} 视频标题{标题}.mp4"
        elif filename.startswith("商品 id") and " 视频标题" in filename:
            try:
                # 提取商品ID
                id_part = filename.split(" 视频标题")[0]
                product_id = id_part.replace("商品 id", "")
                
                # 提取标题（去掉.mp4扩展名）
                title_part = filename.split(" 视频标题")[1]
                title = title_part.replace(".mp4", "")
                
                return product_id, title
            except Exception as e:
                self.log_message.emit(f"解析带空格格式文件名出错: {filename}, 错误: {str(e)}")
        
        # 情况4: 商品ID后有空格且没有视频标题空格的情况 "商品 id{ID}视频标题{标题}.mp4"
        elif filename.startswith("商品 id") and "视频标题" in filename:
            try:
                # 提取商品ID
                id_part = filename.split("视频标题")[0]
                product_id = id_part.replace("商品 id", "")
                
                # 提取标题（去掉.mp4扩展名）
                title_part = filename.split("视频标题")[1]
                title = title_part.replace(".mp4", "")
                
                return product_id, title
            except Exception as e:
                self.log_message.emit(f"解析带空格紧凑格式文件名出错: {filename}, 错误: {str(e)}")
                
        self.log_message.emit(f"无法解析文件名格式: {filename}")
        return None, None

    def get_commission_rate(self, product_id):
        """调用快手API获取佣金率"""
        max_retries = 3
        retry_delay = 10  # 初始重试延迟10秒
        
        for attempt in range(max_retries):
            try:
                api_url = self.config.get('kuaishou', 'api_url')
                cookie = self.config.get('kuaishou', 'cookie')
                
                headers = {
                    'Referer': 'https://cps.kwaixiaodian.com/pc/promoter/selection-center/home',
                    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 QuarkPC/4.5.5.535',
                    'Trace-id': '1.0.0.1759207323819.12',
                    'Origin': 'https://cps.kwaixiaodian.com',
                    'Kpf': 'PC_WEB',
                    'Ktrace-str': '3|My40NTgzNjk4Mjg2NzM2NzY5Ljg4NzU5Nzc5LjE3NTkyMDczMjM4MjAuMTAyNQ==|My4wNTgzNjk4Mjg2NzM2NzY5Ljc1NDgzOTQ2LjE3NTkyMDczMjM4MjAuMTAyNA==|0|plateco-kfx-service|plateco|true|src:Js,seqn:7456,rsi:52a1f2d9-83d9-4bc3-b15d-020622163cdb,path:/pc/promoter/selection-center/home,rpi:9db83bca4a',
                    'Sec-ch-ua': '?0',
                    'Sec-ch-ua-platform': '"macOS"',
                    'Sec-fetch-dest': 'empty',
                    'Sec-fetch-mode': 'cors',
                    'Sec-fetch-site': 'same-origin',
                    'Cookie': cookie,
                    'Content-Type': 'application/json',
                    'Accept': '*/*',
                    'Host': 'cps.kwaixiaodian.com',
                    'Connection': 'keep-alive'
                }
                
                data = {
                    "orderType": 0,
                    "channelId": 99,
                    "keyWord": product_id,
                    "pcursor": "1_1",
                    "requestType": 0
                }
                
                response = requests.post(api_url, headers=headers, json=data, timeout=30)
                
                # 检查HTTP状态码
                if response.status_code != 200:
                    self.log_message.emit(f"快手API HTTP错误: {response.status_code}, 商品ID: {product_id}")
                    try:
                        error_response = response.json()
                        self.log_message.emit(f"错误响应内容: {json.dumps(error_response, ensure_ascii=False)}")
                    except:
                        self.log_message.emit(f"响应文本内容: {response.text[:500]}")
                    
                    if response.status_code == 429:  # 限流错误
                        wait_time = retry_delay * (2 ** attempt)  # 指数退避
                        self.log_message.emit(f"检测到限流，等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                        time.sleep(wait_time)
                        continue
                    return None
                    
                # 解析JSON响应
                try:
                    result = response.json()
                except json.JSONDecodeError as e:
                    self.log_message.emit(f"快手API响应JSON解析失败: {str(e)}, 商品ID: {product_id}")
                    self.log_message.emit(f"响应文本内容: {response.text[:500]}")
                    return None
                
                # 检查API返回的结果状态
                if result.get("result") != 1:
                    error_msg = result.get("errorMsg", "未知错误")
                    self.log_message.emit(f"快手API返回错误: {error_msg}, 商品ID: {product_id}")
                    
                    # 检查是否是限流错误
                    if "限流" in error_msg or "too many requests" in error_msg.lower() or "rate limit" in error_msg.lower():
                        wait_time = retry_delay * (2 ** attempt)  # 指数退避
                        self.log_message.emit(f"检测到限流，等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                        time.sleep(wait_time)
                        continue
                    
                    # 添加更详细的错误信息记录
                    self.log_message.emit(f"完整错误响应: {json.dumps(result, ensure_ascii=False)}")
                    return None
                
                # 检查数据是否存在
                if not result.get("data"):
                    self.log_message.emit(f"快手API返回空data字段, 商品ID: {product_id}")
                    self.log_message.emit(f"完整响应数据: {json.dumps(result, ensure_ascii=False)}")
                    return None
                
                # 检查数据数组是否为空
                if len(result["data"]) == 0:
                    self.log_message.emit(f"快手API返回空data数组, 商品ID: {product_id}")
                    self.log_message.emit(f"完整响应数据: {json.dumps(result, ensure_ascii=False)}")
                    return None
                    
                # 提取佣金率
                first_item = result["data"][0]
                commission_rate = first_item.get("commissionRate")
                if commission_rate is None:
                    self.log_message.emit(f"快手API返回数据中未找到佣金率字段, 商品ID: {product_id}")
                    self.log_message.emit(f"第一个商品数据: {json.dumps(first_item, ensure_ascii=False)}")
                    return None
                    
                return str(commission_rate)
                
            except requests.exceptions.Timeout:
                self.log_message.emit(f"快手API请求超时, 商品ID: {product_id}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return None
            except requests.exceptions.ConnectionError:
                self.log_message.emit(f"快手API连接错误, 商品ID: {product_id}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return None
            except requests.exceptions.RequestException as e:
                self.log_message.emit(f"快手API请求异常: {str(e)}, 商品ID: {product_id}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return None
            except Exception as e:
                self.log_message.emit(f"获取佣金率失败: {product_id}, 错误: {str(e)}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return None
        
        self.log_message.emit(f"快手API请求失败，已达到最大重试次数 ({max_retries})，商品ID: {product_id}")
        return None

    def get_product_category(self, title):
        """调用模型API获取商品类目"""
        # 获取使用的平台
        platform = self.config.get('model_api', 'platform', fallback='siliconflow')
        
        if platform == 'siliconflow':
            return self._get_category_from_siliconflow(title)
        elif platform == 'dashscope':
            return self._get_category_from_dashscope(title)
        else:
            self.log_message.emit(f"不支持的模型API平台: {platform}")
            return "不确定"

    def _get_category_from_siliconflow(self, title):
        """调用硅基流动大模型API获取商品类目"""
        max_retries = 3
        retry_delay = 5  # 初始重试延迟5秒
        
        for attempt in range(max_retries):
            try:
                api_url = self.config.get('model_api', 'siliconflow_api_url')
                api_key = self.config.get('model_api', 'siliconflow_api_key')
                model_name = self.config.get('model_api', 'siliconflow_model', fallback='deepseek-ai/DeepSeek-V3.1-Terminus')
                enable_thinking = self.config.getboolean('model_api', 'siliconflow_enable_thinking', fallback=False)
                simplify = self.config.getboolean('model_api', 'siliconflow_simplify', fallback=True)
                
                headers = {
                    'Authorization': f'Bearer {api_key}',
                    'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
                    'Content-Type': 'application/json',
                    'Accept': '*/*',
                    'Host': 'api.siliconflow.cn',
                    'Connection': 'keep-alive'
                }
                
                data = {
                    "top_p": 0.7,
                    "model": model_name,
                    "simplify": simplify,
                    "messages": [
                        {
                            "content": "你是电商领域的专家， 需求：用户输入一个标题，你就给我返回一个类目名称 要求： 一不能出任何一个错， 确定才输出结果，如果你无法确定，或者没有把握，请返回，不确定三个字 二，只能在下面 12 个类目里面输出一个名称： 食品饮料、家居百货、女装女鞋、美妆护肤、个护清洁、医疗保健、母婴玩具、茶酒生鲜、男装男鞋、运动户外、数码家电、珠宝配饰",
                            "role": "system"
                        },
                        {
                            "content": title,
                            "role": "user"
                        }
                    ],
                    "response_format": {
                        "type": "text"
                    }
                }
                
                # 只有启用思考过程时才添加相关参数
                if enable_thinking:
                    data["enable_thinking"] = True
                    data["thinking_budget"] = 4096
                
                response = requests.post(api_url, headers=headers, json=data, timeout=30)
                
                # 检查HTTP状态码
                if response.status_code != 200:
                    # 记录详细的错误信息
                    try:
                        error_detail = response.json()
                        self.log_message.emit(f"硅基流动API HTTP错误: {response.status_code}, 标题: {title}")
                        self.log_message.emit(f"错误详情: {error_detail}")
                    except:
                        self.log_message.emit(f"硅基流动API HTTP错误: {response.status_code}, 标题: {title}")
                        self.log_message.emit(f"响应内容: {response.text[:500]}")
                    
                    if response.status_code == 429:  # 限流错误
                        wait_time = retry_delay * (2 ** attempt)  # 指数退避
                        self.log_message.emit(f"检测到硅基流动API限流，等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                        time.sleep(wait_time)
                        continue
                    elif response.status_code == 400:
                        # 400错误通常是参数问题，不重试
                        return "不确定"
                    return "不确定"
                    
                result = response.json()
                
                # 检查是否有错误信息
                if result.get("error"):
                    error_msg = result.get("error", {}).get("message", "未知错误")
                    self.log_message.emit(f"硅基流动API返回错误: {error_msg}, 标题: {title}")
                    
                    # 检查是否是限流错误
                    if "rate limit" in error_msg.lower() or "限流" in error_msg:
                        wait_time = retry_delay * (2 ** attempt)  # 指数退避
                        self.log_message.emit(f"检测到硅基流动API限流，等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                        time.sleep(wait_time)
                        continue
                    
                    return "不确定"
                
                # 检查choices是否存在
                if not result.get("choices"):
                    self.log_message.emit(f"硅基流动API返回空数据, 标题: {title}")
                    return "不确定"
                    
                # 提取类目
                category = result["choices"][0]["message"]["content"]
                if category is None:
                    self.log_message.emit(f"硅基流动API返回数据中未找到类目, 标题: {title}")
                    return "不确定"
                    
                return category.strip()
                
            except requests.exceptions.Timeout:
                self.log_message.emit(f"硅基流动API请求超时, 标题: {title}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return "不确定"
            except requests.exceptions.ConnectionError:
                self.log_message.emit(f"硅基流动API连接错误, 标题: {title}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return "不确定"
            except requests.exceptions.RequestException as e:
                self.log_message.emit(f"硅基流动API请求异常: {str(e)}, 标题: {title}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return "不确定"
            except Exception as e:
                self.log_message.emit(f"获取商品类目失败: {title}, 错误: {str(e)}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return "不确定"
        
        self.log_message.emit(f"硅基流动API请求失败，已达到最大重试次数 ({max_retries})，标题: {title}")
        return "不确定"

    def _get_category_from_dashscope(self, title):
        """调用百炼平台API获取商品类目"""
        max_retries = 3
        retry_delay = 5  # 初始重试延迟5秒
        
        for attempt in range(max_retries):
            try:
                api_url = self.config.get('model_api', 'dashscope_api_url')
                api_key = self.config.get('model_api', 'dashscope_api_key')
                model_name = self.config.get('model_api', 'dashscope_model', fallback='qwen-plus')
                simplify = self.config.getboolean('model_api', 'dashscope_simplify', fallback=True)
                
                headers = {
                    'Authorization': f'Bearer {api_key}',
                    'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
                    'Content-Type': 'application/json',
                    'Accept': '*/*',
                    'Host': 'dashscope.aliyuncs.com',
                    'Connection': 'keep-alive'
                }
                
                data = {
                    "model": model_name,
                    "simplify": simplify,
                    "messages": [
                        {
                            "role": "system",
                            "content": "你是电商领域的专家， 需求：       用户输入一个标题，你就给我返回一个类目名称 要求： 一不能出任何一个错， 确定才输出结果，如果你无法确定，，或者没有把握，请返回，不确定三个字 二，只能在下面 12 个类目里面输出一个名称，！ 食品饮料, 家居百货 , 女装女鞋  美妆护肤 , 个护清洁 , 医疗保健 , 母婴玩具 , 茶酒生鲜 , 男装男鞋 , 运动户外 , 数码家电 ."
                        },
                        {
                            "role": "user", 
                            "content": title
                        }
                    ]
                }
                
                response = requests.post(api_url, headers=headers, json=data, timeout=30)
                
                # 检查HTTP状态码
                if response.status_code != 200:
                    self.log_message.emit(f"百炼平台API HTTP错误: {response.status_code}, 标题: {title}")
                    if response.status_code == 429:  # 限流错误
                        wait_time = retry_delay * (2 ** attempt)  # 指数退避
                        self.log_message.emit(f"检测到百炼平台API限流，等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                        time.sleep(wait_time)
                        continue
                    return "不确定"
                    
                result = response.json()
                
                # 检查是否有错误信息
                if result.get("error"):
                    error_msg = result.get("error", {}).get("message", "未知错误")
                    self.log_message.emit(f"百炼平台API返回错误: {error_msg}, 标题: {title}")
                    
                    # 检查是否是限流错误
                    if "rate limit" in error_msg.lower() or "限流" in error_msg:
                        wait_time = retry_delay * (2 ** attempt)  # 指数退避
                        self.log_message.emit(f"检测到百炼平台API限流，等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                        time.sleep(wait_time)
                        continue
                    
                    return "不确定"
                
                # 检查choices是否存在
                if not result.get("choices"):
                    self.log_message.emit(f"百炼平台API返回空数据, 标题: {title}")
                    return "不确定"
                    
                # 提取类目
                category = result["choices"][0]["message"]["content"]
                if category is None:
                    self.log_message.emit(f"百炼平台API返回数据中未找到类目, 标题: {title}")
                    return "不确定"
                    
                return category.strip()
                
            except requests.exceptions.Timeout:
                self.log_message.emit(f"百炼平台API请求超时, 标题: {title}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return "不确定"
            except requests.exceptions.ConnectionError:
                self.log_message.emit(f"百炼平台API连接错误, 标题: {title}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return "不确定"
            except requests.exceptions.RequestException as e:
                self.log_message.emit(f"百炼平台API请求异常: {str(e)}, 标题: {title}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return "不确定"
            except Exception as e:
                self.log_message.emit(f"获取商品类目失败: {title}, 错误: {str(e)}")
                if attempt < max_retries - 1:
                    wait_time = retry_delay * (2 ** attempt)  # 指数退避
                    self.log_message.emit(f"等待 {wait_time} 秒后重试... (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(wait_time)
                    continue
                return "不确定"
        
        self.log_message.emit(f"百炼平台API请求失败，已达到最大重试次数 ({max_retries})，标题: {title}")
        return "不确定"


class ConfigWindow(QWidget):
    def __init__(self, config):
        super().__init__()
        self.config = config
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()
        
        # 路径配置组
        path_group = QGroupBox("路径配置")
        path_layout = QFormLayout()
        path_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)  # 让输入框可以扩展
        
        self.source_folder_edit = QLineEdit()
        self.source_folder_edit.setText(self.config.get('paths', 'source_folder', fallback=''))
        self.source_folder_edit.setMinimumWidth(300)  # 设置最小宽度
        self.source_folder_btn = QPushButton("选择")
        self.source_folder_btn.clicked.connect(self.select_source_folder)
        source_layout = QHBoxLayout()
        source_layout.addWidget(self.source_folder_edit)
        source_layout.addWidget(self.source_folder_btn)
        
        self.processed_folder_edit = QLineEdit()
        self.processed_folder_edit.setText(self.config.get('paths', 'processed_folder', fallback='已处理'))
        self.processed_folder_edit.setMinimumWidth(300)  # 设置最小宽度
        self.processed_folder_btn = QPushButton("选择")
        self.processed_folder_btn.clicked.connect(self.select_processed_folder)
        processed_layout = QHBoxLayout()
        processed_layout.addWidget(self.processed_folder_edit)
        processed_layout.addWidget(self.processed_folder_btn)
        
        path_layout.addRow("源视频文件夹:", source_layout)
        path_layout.addRow("已处理文件夹:", processed_layout)
        
        path_group.setLayout(path_layout)
        layout.addWidget(path_group)
        
        # API配置组
        api_group = QGroupBox("API配置")
        api_layout = QFormLayout()
        api_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)  # 让输入框可以扩展
        
        self.request_interval_spin = QSpinBox()
        self.request_interval_spin.setRange(1, 300)
        self.request_interval_spin.setValue(self.config.getint('api', 'request_interval', fallback=10))
        api_layout.addRow("请求间隔(秒):", self.request_interval_spin)
        
        # 添加多线程配置
        self.max_workers_spin = QSpinBox()
        self.max_workers_spin.setRange(1, 10)
        self.max_workers_spin.setValue(self.config.getint('threading', 'max_workers', fallback=1))
        api_layout.addRow("线程数量:", self.max_workers_spin)
        
        # 添加智能类目配置
        self.enable_smart_category_check = QCheckBox("启用智能类目分类")
        self.enable_smart_category_check.setChecked(self.config.getboolean('processing', 'enable_smart_category', fallback=True))
        api_layout.addRow("分类模式:", self.enable_smart_category_check)
        
        self.kuaishou_api_edit = QLineEdit()
        self.kuaishou_api_edit.setText(self.config.get('kuaishou', 'api_url', fallback='https://cps.kwaixiaodian.com/gateway/distribute/match/selection/home/query/item/list'))
        self.kuaishou_api_edit.setMinimumWidth(400)  # 设置最小宽度
        api_layout.addRow("快手API URL:", self.kuaishou_api_edit)
        
        self.kuaishou_cookie_edit = QLineEdit()
        self.kuaishou_cookie_edit.setText(self.config.get('kuaishou', 'cookie', fallback=''))
        self.kuaishou_cookie_edit.setMinimumWidth(400)  # 设置最小宽度
        api_layout.addRow("快手API Cookie:", self.kuaishou_cookie_edit)
        
        # 添加平台选择下拉框
        self.platform_combo = QComboBox()
        self.platform_combo.addItem("硅基流动 (siliconflow)")
        self.platform_combo.addItem("百炼平台 (dashscope)")
        current_platform = self.config.get('model_api', 'platform', fallback='siliconflow')
        if current_platform == 'dashscope':
            self.platform_combo.setCurrentIndex(1)
        else:
            self.platform_combo.setCurrentIndex(0)
        # 连接平台选择变化信号
        self.platform_combo.currentIndexChanged.connect(self.on_platform_changed)
        api_layout.addRow("模型平台:", self.platform_combo)
        
        # 使用堆叠布局管理不同平台的配置
        self.platform_stack = QStackedWidget()
        
        # 硅基流动配置页面
        siliconflow_widget = QWidget()
        siliconflow_layout = QFormLayout()
        siliconflow_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
        
        self.siliconflow_api_edit = QLineEdit()
        self.siliconflow_api_edit.setText(self.config.get('model_api', 'siliconflow_api_url', fallback='https://api.siliconflow.cn/v1/chat/completions'))
        self.siliconflow_api_edit.setMinimumWidth(400)
        siliconflow_layout.addRow("API URL:", self.siliconflow_api_edit)
        
        self.siliconflow_key_edit = QLineEdit()
        self.siliconflow_key_edit.setText(self.config.get('model_api', 'siliconflow_api_key', fallback=''))
        self.siliconflow_key_edit.setMinimumWidth(400)
        siliconflow_layout.addRow("API密钥:", self.siliconflow_key_edit)
        
        self.siliconflow_model_edit = QLineEdit()
        self.siliconflow_model_edit.setText(self.config.get('model_api', 'siliconflow_model', fallback='deepseek-ai/DeepSeek-V3.1-Terminus'))
        self.siliconflow_model_edit.setMinimumWidth(400)
        siliconflow_layout.addRow("模型名称:", self.siliconflow_model_edit)
        
        self.siliconflow_thinking_check = QCheckBox("启用思考过程")
        self.siliconflow_thinking_check.setChecked(self.config.getboolean('model_api', 'siliconflow_enable_thinking', fallback=False))
        siliconflow_layout.addRow("思考过程:", self.siliconflow_thinking_check)
        
        self.siliconflow_simplify_check = QCheckBox("简化响应")
        self.siliconflow_simplify_check.setChecked(self.config.getboolean('model_api', 'siliconflow_simplify', fallback=True))
        siliconflow_layout.addRow("简化响应:", self.siliconflow_simplify_check)
        
        siliconflow_widget.setLayout(siliconflow_layout)
        
        # 百炼平台配置页面
        dashscope_widget = QWidget()
        dashscope_layout = QFormLayout()
        dashscope_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
        
        self.dashscope_api_edit = QLineEdit()
        self.dashscope_api_edit.setText(self.config.get('model_api', 'dashscope_api_url', fallback='https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions'))
        self.dashscope_api_edit.setMinimumWidth(400)
        dashscope_layout.addRow("API URL:", self.dashscope_api_edit)
        
        self.dashscope_key_edit = QLineEdit()
        self.dashscope_key_edit.setText(self.config.get('model_api', 'dashscope_api_key', fallback=''))
        self.dashscope_key_edit.setMinimumWidth(400)
        dashscope_layout.addRow("API密钥:", self.dashscope_key_edit)
        
        self.dashscope_model_edit = QLineEdit()
        self.dashscope_model_edit.setText(self.config.get('model_api', 'dashscope_model', fallback='qwen-plus'))
        self.dashscope_model_edit.setMinimumWidth(400)
        dashscope_layout.addRow("模型名称:", self.dashscope_model_edit)
        
        self.dashscope_simplify_check = QCheckBox("简化响应")
        self.dashscope_simplify_check.setChecked(self.config.getboolean('model_api', 'dashscope_simplify', fallback=True))
        dashscope_layout.addRow("简化响应:", self.dashscope_simplify_check)
        
        dashscope_widget.setLayout(dashscope_layout)
        
        # 添加页面到堆叠布局
        self.platform_stack.addWidget(siliconflow_widget)
        self.platform_stack.addWidget(dashscope_widget)
        
        api_layout.addRow(self.platform_stack)
        
        api_group.setLayout(api_layout)
        layout.addWidget(api_group)
        
        # 保存按钮
        save_btn = QPushButton("保存配置")
        save_btn.clicked.connect(self.save_config)
        layout.addWidget(save_btn)
        
        self.setLayout(layout)
        self.setWindowTitle("配置窗口")
        self.resize(600, 500)  # 调整窗口大小
        
        # 初始化平台显示状态
        self.on_platform_changed(self.platform_combo.currentIndex())

    def select_source_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "选择源视频文件夹")
        if folder:
            self.source_folder_edit.setText(folder)

    def select_processed_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "选择已处理文件夹")
        if folder:
            self.processed_folder_edit.setText(folder)

    def on_platform_changed(self, index):
        """平台选择变化时的处理函数"""
        # 使用堆叠布局切换页面
        self.platform_stack.setCurrentIndex(index)

    def save_config(self):
        # 保存路径配置
        self.config.set('paths', 'source_folder', self.source_folder_edit.text())
        self.config.set('paths', 'processed_folder', self.processed_folder_edit.text())
        
        # 保存API配置
        self.config.set('api', 'request_interval', str(self.request_interval_spin.value()))
        
        # 保存多线程配置
        if not self.config.has_section('threading'):
            self.config.add_section('threading')
        self.config.set('threading', 'max_workers', str(self.max_workers_spin.value()))
        
        # 保存处理配置
        if not self.config.has_section('processing'):
            self.config.add_section('processing')
        self.config.set('processing', 'enable_smart_category', str(self.enable_smart_category_check.isChecked()))
        self.config.set('kuaishou', 'api_url', self.kuaishou_api_edit.text())
        self.config.set('kuaishou', 'cookie', self.kuaishou_cookie_edit.text())
        
        # 保存平台选择
        if self.platform_combo.currentIndex() == 1:
            self.config.set('model_api', 'platform', 'dashscope')
        else:
            self.config.set('model_api', 'platform', 'siliconflow')
            
        # 保存硅基流动配置
        self.config.set('model_api', 'siliconflow_api_url', self.siliconflow_api_edit.text())
        self.config.set('model_api', 'siliconflow_api_key', self.siliconflow_key_edit.text())
        self.config.set('model_api', 'siliconflow_model', self.siliconflow_model_edit.text())
        self.config.set('model_api', 'siliconflow_enable_thinking', str(self.siliconflow_thinking_check.isChecked()))
        self.config.set('model_api', 'siliconflow_simplify', str(self.siliconflow_simplify_check.isChecked()))
        
        # 保存百炼平台配置
        self.config.set('model_api', 'dashscope_api_url', self.dashscope_api_edit.text())
        self.config.set('model_api', 'dashscope_api_key', self.dashscope_key_edit.text())
        self.config.set('model_api', 'dashscope_model', self.dashscope_model_edit.text())
        self.config.set('model_api', 'dashscope_simplify', str(self.dashscope_simplify_check.isChecked()))
        
        # 保存到文件
        with open('config.ini', 'w', encoding='utf-8') as configfile:
            self.config.write(configfile)
        
        QMessageBox.information(self, "提示", "配置已保存")
        
        # 保存成功后关闭配置窗口
        self.close()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.config = configparser.ConfigParser()
        self.load_config()
        self.processor = None
        self.init_ui()

    def load_config(self):
        # 默认配置
        if not self.config.has_section('paths'):
            self.config.add_section('paths')
        if not self.config.has_option('paths', 'source_folder'):
            self.config.set('paths', 'source_folder', '')
        if not self.config.has_option('paths', 'processed_folder'):
            self.config.set('paths', 'processed_folder', '已处理')
            
        if not self.config.has_section('api'):
            self.config.add_section('api')
        if not self.config.has_option('api', 'request_interval'):
            self.config.set('api', 'request_interval', '10')
            
        if not self.config.has_section('kuaishou'):
            self.config.add_section('kuaishou')
        if not self.config.has_option('kuaishou', 'api_url'):
            self.config.set('kuaishou', 'api_url', 'https://cps.kwaixiaodian.com/gateway/distribute/match/selection/home/query/item/list')
        if not self.config.has_option('kuaishou', 'cookie'):
            self.config.set('kuaishou', 'cookie', '')
            
        if not self.config.has_section('siliconflow'):
            self.config.add_section('siliconflow')
        if not self.config.has_option('siliconflow', 'api_url'):
            self.config.set('siliconflow', 'api_url', 'https://api.siliconflow.cn/v1/chat/completions')
        if not self.config.has_option('siliconflow', 'api_key'):
            self.config.set('siliconflow', 'api_key', '')
            
        # 添加model_api部分的默认配置
        if not self.config.has_section('model_api'):
            self.config.add_section('model_api')
        if not self.config.has_option('model_api', 'platform'):
            self.config.set('model_api', 'platform', 'siliconflow')
        if not self.config.has_option('model_api', 'siliconflow_api_url'):
            self.config.set('model_api', 'siliconflow_api_url', 'https://api.siliconflow.cn/v1/chat/completions')
        if not self.config.has_option('model_api', 'siliconflow_api_key'):
            self.config.set('model_api', 'siliconflow_api_key', '')
        if not self.config.has_option('model_api', 'siliconflow_model'):
            self.config.set('model_api', 'siliconflow_model', 'deepseek-ai/DeepSeek-V3.1-Terminus')
        if not self.config.has_option('model_api', 'siliconflow_enable_thinking'):
            self.config.set('model_api', 'siliconflow_enable_thinking', 'false')
        if not self.config.has_option('model_api', 'siliconflow_simplify'):
            self.config.set('model_api', 'siliconflow_simplify', 'true')
        if not self.config.has_option('model_api', 'dashscope_api_url'):
            self.config.set('model_api', 'dashscope_api_url', 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions')
        if not self.config.has_option('model_api', 'dashscope_api_key'):
            self.config.set('model_api', 'dashscope_api_key', '')
        if not self.config.has_option('model_api', 'dashscope_model'):
            self.config.set('model_api', 'dashscope_model', 'qwen-plus')
        if not self.config.has_option('model_api', 'dashscope_simplify'):
            self.config.set('model_api', 'dashscope_simplify', 'true')
        
        # 添加多线程配置
        if not self.config.has_section('threading'):
            self.config.add_section('threading')
        if not self.config.has_option('threading', 'max_workers'):
            self.config.set('threading', 'max_workers', '1')
        
        # 添加处理配置
        if not self.config.has_section('processing'):
            self.config.add_section('processing')
        if not self.config.has_option('processing', 'enable_smart_category'):
            self.config.set('processing', 'enable_smart_category', 'true')
        
        # 尝试从文件加载配置
        if os.path.exists('config.ini'):
            self.config.read('config.ini', encoding='utf-8')

    def init_ui(self):
        self.setWindowTitle('快手运营')
        self.setGeometry(100, 100, 1200, 700)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        main_layout = QVBoxLayout()
        central_widget.setLayout(main_layout)
        
        # 创建标签页
        tab_widget = QTabWidget()
        
        # 视频处理标签页
        video_processing_tab = QWidget()
        video_processing_layout = QVBoxLayout()
        
        # 控制按钮
        control_layout = QHBoxLayout()
        self.start_btn = QPushButton("开始处理")
        self.start_btn.clicked.connect(self.start_processing)
        self.stop_btn = QPushButton("停止处理")
        self.stop_btn.clicked.connect(self.stop_processing)
        self.stop_btn.setEnabled(False)
        self.config_btn = QPushButton("配置")
        self.config_btn.clicked.connect(self.open_config)
        
        control_layout.addWidget(self.start_btn)
        control_layout.addWidget(self.stop_btn)
        control_layout.addWidget(self.config_btn)
        control_layout.addStretch()
        
        video_processing_layout.addLayout(control_layout)
        
        # 进度信息
        progress_layout = QHBoxLayout()
        self.progress_label = QLabel("当前进度: 0/0")
        self.status_label = QLabel("状态: 等待开始")
        progress_layout.addWidget(self.progress_label)
        progress_layout.addWidget(self.status_label)
        progress_layout.addStretch()
        
        video_processing_layout.addLayout(progress_layout)
        
        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)
        video_processing_layout.addWidget(self.progress_bar)
        
        # 日志显示
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        video_processing_layout.addWidget(self.log_text)
        
        video_processing_tab.setLayout(video_processing_layout)
        tab_widget.addTab(video_processing_tab, "视频分类")
        
        # 清除重复视频标签页
        duplicate_cleaner_tab = DuplicateCleanerDialog(self)
        tab_widget.addTab(duplicate_cleaner_tab, "清除重复视频")
        
        # 视频搬运工具标签页
        video_mover_tab = VideoMoverTool()
        tab_widget.addTab(video_mover_tab, "视频搬运工具")
        
        main_layout.addWidget(tab_widget)
        
    def start_processing(self):
        # 检查必要配置
        source_folder = self.config.get('paths', 'source_folder')
        if not source_folder or not os.path.exists(source_folder):
            QMessageBox.warning(self, "警告", "请先设置有效的源视频文件夹路径")
            return
            
        # 创建处理器
        self.processor = VideoProcessor(self.config)
        self.processor.progress_updated.connect(self.update_progress)
        self.processor.log_message.connect(self.add_log)
        self.processor.processing_finished.connect(self.processing_finished)
        self.processor.error_occurred.connect(self.processing_error)
        
        # 更新UI状态
        self.start_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        self.status_label.setText("状态: 处理中...")
        
        # 开始处理
        self.processor.start()

    def stop_processing(self):
        if self.processor and self.processor.isRunning():
            self.processor.stop()
            self.status_label.setText("状态: 正在停止...")

    def open_config(self):
        self.config_window = ConfigWindow(self.config)
        self.config_window.show()
        
    def open_duplicate_cleaner(self):
        """打开重复视频清理对话框"""
        self.duplicate_cleaner_dialog = DuplicateCleanerDialog(self)
        self.duplicate_cleaner_dialog.show()

    def update_progress(self, current, total, filename):
        self.progress_bar.setMaximum(total)
        self.progress_bar.setValue(current)
        self.progress_label.setText(f"当前进度: {current}/{total}")
        self.status_label.setText(f"状态: 处理中 - {filename}")

    def add_log(self, message):
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        # 检查消息类型并设置相应颜色（按优先级顺序检查）
        
        # 首先检查emoji和特定标识
        if "⚠️" in message or "💡" in message:
            # 橙色显示警告/提示消息
            self.log_text.insertHtml(f'<span style="color:#F39C12">[{timestamp}] {message}</span><br>')
        elif "✅" in message or "🎉" in message:
            # 绿色显示成功消息
            self.log_text.insertHtml(f'<span style="color:#27AE60">[{timestamp}] {message}</span><br>')
        elif "❌" in message or "🚫" in message:
            # 红色显示错误消息
            self.log_text.insertHtml(f'<span style="color:#E74C3C">[{timestamp}] {message}</span><br>')
        
        # 然后检查关键词（按优先级顺序 - 成功类关键词优先级最高）
        elif any(keyword in message for keyword in ["成功处理", "成功获取", "成功"]):
            # 柔和的绿色显示成功消息（优先级最高）
            self.log_text.insertHtml(f'<span style="color:#27AE60">[{timestamp}] {message}</span><br>')
        elif any(keyword in message for keyword in ["开始", "启动", "初始化"]):
            # 蓝色显示启动消息（第二优先级）
            self.log_text.insertHtml(f'<span style="color:#3498DB">[{timestamp}] {message}</span><br>')
        elif any(keyword in message for keyword in ["找到", "完成"]):
            # 柔和的绿色显示找到/完成消息
            self.log_text.insertHtml(f'<span style="color:#27AE60">[{timestamp}] {message}</span><br>')
        elif any(keyword in message for keyword in ["警告", "注意", "提示", "等待", "未找到", "建议", "检查"]):
            # 橙色显示警告消息
            self.log_text.insertHtml(f'<span style="color:#F39C12">[{timestamp}] {message}</span><br>')
        elif any(keyword in message for keyword in ["错误", "失败", "无法", "出错", "HTTP错误", "请求失败", "超时", "连接错误"]):
            # 红色显示错误消息（优先级最低）
            self.log_text.insertHtml(f'<span style="color:#E74C3C">[{timestamp}] {message}</span><br>')
        else:
            # 深灰色显示普通消息（更柔和的颜色）
            self.log_text.insertHtml(f'<span style="color:#2C3E50">[{timestamp}] {message}</span><br>')
        # 自动滚动到底部
        self.log_text.moveCursor(QTextCursor.End)

    def processing_finished(self):
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)
        
        # 检查是否有文件被处理
        if self.processor and hasattr(self.processor, 'processed_count') and self.processor.processed_count == 0:
            # 未处理任何文件
            self.status_label.setText("状态: 未找到文件 - 已停止")
            self.progress_bar.setValue(0)
            self.progress_label.setText("进度: 0/0")
            QMessageBox.information(self, "提示", 
                "📁 未找到任何视频文件！\n\n"
                "请检查：\n"
                "• 源文件夹路径是否正确\n"
                "• 文件夹中是否包含 .mp4 格式的视频文件\n"
                "• 程序会自动递归搜索所有子文件夹\n\n"
                "💡 请添加视频文件后重新开始处理！")
        else:
            # 正常处理完成
            self.status_label.setText("状态: 处理完成")
            QMessageBox.information(self, "提示", "✅ 视频处理完成！")

    def processing_error(self, error_msg):
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)
        self.status_label.setText("状态: 发生错误")
        QMessageBox.critical(self, "错误", f"处理过程中发生错误:\n{error_msg}")


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()